package com.growcontrol.client.netty;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.io.IOException;
import java.net.UnknownHostException;
import java.security.cert.CertificateException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.net.ssl.SSLException;
import com.growcontrol.client.configs.SavedProfile;
import com.poixson.commonjava.Utils.Keeper;
import com.poixson.commonjava.Utils.utilsThread;
import com.poixson.commonjava.Utils.xCloseableMany;
import com.poixson.commonjava.Utils.xTimeU;
import com.poixson.commonjava.Utils.threads.xThreadFactory;
import com.poixson.commonjava.xLogger.xLog;
/*
NetClientManager
-> NetClient
-> ClientSocketState
-> NetClientHandler
PacketState
*/
public class NetClientManager implements xCloseableMany {
private static final String LOG_NAME = "NET";
// manager instance
private static volatile NetClientManager instance = null;
private static final Object instanceLock = new Object();
// clients
protected final ConcurrentMap<String, NetClient> netClients =
new ConcurrentHashMap<String, NetClient>();
// thread group
protected final EventLoopGroup workGroup;
// ssl
protected final SslContext sslContext;
public static NetClient get(final SavedProfile profile) {
if(profile == null) throw new NullPointerException("profile argument is required!");
try {
return get().getClient(profile);
} catch (UnknownHostException e) {
log().trace(e);
return null;
} catch (InterruptedException e) {
log().trace(e);
return null;
}
}
public static NetClientManager get() {
if(instance == null) {
synchronized(instanceLock) {
try {
if(instance == null)
instance = new NetClientManager();
} catch (SSLException e) {
instance = null;
log().trace(e);
return null;
} catch (CertificateException e) {
instance = null;
log().trace(e);
return null;
}
}
}
return instance;
}
protected NetClientManager() throws SSLException, CertificateException {
Keeper.add(this);
// thread group
final int cores = Runtime.getRuntime().availableProcessors();
this.workGroup = new NioEventLoopGroup(cores, new xThreadFactory("netty-work", true));
// this method is bad for servers
// bossGroup = Executors.newCachedThreadPool();
// workGroup = Executors.newCachedThreadPool();
// this method is used in netty examples
// bossGroup = new NioEventLoopGroup();
// workGroup = new NioEventLoopGroup();
// ssl
final SelfSignedCertificate cert = new SelfSignedCertificate();
this.sslContext = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()).build();
//TODO:
// // ssl packet handler
// this.bootstrap.childHandler(
// new ServerSocketChannelInitializer(this.log(), this.sslContext)
// );
// if (this.log().isLoggable(xLevel.DETAIL)) {
// NettyDetailedLogger.Install(this.log());
// }
}
public NetClient getClient(final SavedProfile profile)
throws UnknownHostException, InterruptedException {
if(profile == null) throw new NullPointerException("profile argument is required!");
final String key = profile.toString();
// get existing client
{
final NetClient client = this.netClients.get(key);
if(client != null && !client.isClosed())
return client;
}
// new client
synchronized(this.netClients){
// check once more
{
final NetClient client = this.netClients.get(key);
if(client != null) {
if(client.isClosed())
this.ClearClosed();
else
return client;
}
}
// new client
{
final NetClient client = new NetClient(profile);
this.netClients.put(key, client);
return client;
}
}
}
@Override
public boolean isClosed() {
throw new UnsupportedOperationException();
}
@Override
public void close() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void CloseAll() {
if(this.netClients.size() == 0)
return;
log().info("Closing socket connections..");
int clientsCount = 0;
int socketsCount = 0;
synchronized(this.netClients) {
final Collection<NetClient> clients = this.netClients.values();
// close clients
{
final Set<ChannelFuture> futureCloses = new HashSet<ChannelFuture>();
final Iterator<NetClient> it = clients.iterator();
while(it.hasNext()) {
final NetClient client = it.next();
if(!client.isClosed())
clientsCount++;
futureCloses.add(
client.closeSoon()
);
}
// wait for clients to stop connections
for(final ChannelFuture future : futureCloses) {
try {
future.sync();
} catch (InterruptedException e) {
log().trace(e);
break;
}
}
}
// wait a moment
utilsThread.Sleep(50L);
// close sockets
{
log().info("Closing sockets..");
final Iterator<NetClient> it = clients.iterator();
while(it.hasNext()) {
final NetClient client = it.next();
socketsCount += client.getSocketsCount();
client.CloseAll();
}
}
this.ClearClosed();
}
if(clientsCount > 0) {
log().info(
(new StringBuilder())
.append("Closed ")
.append(clientsCount)
.append(" socket clients, and ")
.append(socketsCount)
.append(" sockets")
.toString()
);
}
// stop thread pool
this.workGroup.shutdownGracefully(
500L,
800L,
xTimeU.MS
);
}
public void ClearClosed() {
if(this.netClients.size() == 0)
return;
synchronized(this.netClients) {
final Iterator<NetClient> it = this.netClients.values().iterator();
while(it.hasNext()) {
final NetClient client = it.next();
if(client.isClosed())
this.netClients.remove(client.getClientKey());
}
}
}
// logger
private static volatile xLog _log = null;
public static xLog log() {
if(_log == null)
_log = xLog.getRoot(LOG_NAME);
return _log;
}
}